﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Mail;
using System.Net;
using MySql.Data.MySqlClient;
using System.Threading;
using System.Net.Sockets;
using System.Windows.Forms;
using MySql.Data.Types;

namespace Controller
{
    class Notification
    {
        MailMessage mail;
        SmtpClient smtp;
        List<Threshold> thresholds;
        List<CurrentData> currentDataList;
        List<VineyardBlock> vineyardBlocks;
        List<NetworkStream> clientStreams;
        

        /// <summary>
        /// Name: Notification constuct
        /// Author: Morgan Hansen & Ray Crego
        /// Details: Constucts a Notification
        /// </summary>
        public Notification()
        {
            vineyardBlocks = getVineyardBlocks();
            getThresholds();

            //for (int i = 0; i < vineyardBlocks.Count; i++)
            // {
            byte i = 0;
            clientStreams = CreateStream(i);
         //b = new BlackBox(i + 1, clientStreams.ElementAt(i));
         // cd = new CurrentData(b, clientStreams.ElementAt(i));
            //currentData.Add(new CurrentData(            
            //make it so we can create the black boxes here 
            currentDataList = createData();

            //}//for
            smtp = new SmtpClient("smtp.gmail.com", 587);
            smtp.EnableSsl = true;
            smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
            smtp.Credentials = new NetworkCredential("vino.tapteil@gmail.com", "tarapass");
        }//constuct

        /// <summary>
        /// Name: CreateStream 
        /// Author: Morgan Hansen
        /// Details: Creates commuacation streams for existing black boxes
        /// </summary>
        /// <param name="BlackBoxID"></param>
        /// <returns>List of NetworkStream</returns>
        private List<NetworkStream> CreateStream(byte BlackBoxID) //untility function
        {
            // will have to ping database for amount of blackboxs
            List<NetworkStream> clientStreams = new List<NetworkStream>();
            NetworkStream clientStream;
            System.Net.Sockets.TcpClient client;
            Int32 port = 23;
            //IPAddress localAddr = IPAddress.Parse("127.0.0.1"); // blackbox address
            IPAddress localAddr = IPAddress.Parse("198.1.3.200"); // blackbox address
            client = new System.Net.Sockets.TcpClient();//create Streams
            client.Connect(localAddr, port);
            clientStream = client.GetStream();
            clientStreams.Add(clientStream);
            ASCIIEncoding encoder = new ASCIIEncoding();
            byte[] buffer;
            buffer = encoder.GetBytes("ffffffw");
           clientStream.Write(buffer, 0, 5);
            Thread.Sleep(500);
            return clientStreams;
        }//CreateStreams

        /// <summary>
        /// Name: CreateData 
        /// Author: Morgan Hansen
        /// </summary>
        /// <returns>List of CurrentData</returns>
        private List<CurrentData> createData()
        {
            BlackBox b;
            CurrentData cd;
            List<int> blackBoxIDs = new List<int>();

            string connectionString = "datasource=localhost;username=tara;password=tarapass;database=vino";
            MySqlConnection connection = new MySqlConnection(connectionString);
            MySqlCommand command = connection.CreateCommand();
            MySqlDataReader Reader;
            command.CommandText = "SELECT BlackBoxID from tblvineyardblock";
            connection.Open();
            Reader = command.ExecuteReader();

            while (Reader.Read())
            {
             if (Reader.GetValue(0) != DBNull.Value)
                blackBoxIDs.Add(Convert.ToInt32(Reader.GetValue(0)));
            }

            connection.Close();

            List<CurrentData> currentDataList = new List<CurrentData>();

            foreach (int id in blackBoxIDs)
            {
                b = new BlackBox(id, clientStreams.ElementAt(id-1));
                cd = new CurrentData(b, clientStreams.ElementAt(id-1));
                currentDataList.Add(cd);
            }
            return currentDataList;
        }//createData

        /// <summary>
        /// Name: CreateData 
        /// Author: Morgan Hansen
        /// gets current data list
        /// </summary>
        /// <returns>List of CurrentData</returns>
        public List<CurrentData> getCurrentDataList()
        {
            return (currentDataList);
        }//getCurrentDataList

        /// <summary>
        /// Author: Ray Crego
        /// Gets Thresholds form DataBase
        /// </summary>
        public void getThresholds()
        {
            List<Threshold> thresholds = new List<Threshold>();

            string connectionString = "datasource=localhost;username=tara;password=tarapass;database=vino";
            MySqlConnection connection = new MySqlConnection(connectionString);
            MySqlCommand command = connection.CreateCommand();
            MySqlDataReader Reader;
            command.CommandText = "SELECT tblthreshold.*, lkupthresholdtype.ThresholdName from tblthreshold LEFT JOIN lkupthresholdtype on lkupthresholdtype.ThresholdTypeID = tblthreshold.ThresholdTypeID";
            connection.Open();
            Reader = command.ExecuteReader();

            Threshold curThreshold;

            while (Reader.Read())
            {
                curThreshold = new Threshold();

                curThreshold.thesholdID = Convert.ToInt32(Reader.GetValue(0));
                //curThreshold.user = new User("", Reader.GetValue(7).ToString());
                curThreshold.thresholdType = new ThresholdType(Reader.GetValue(6).ToString());
                curThreshold.vineyardBlock = new VineyardBlock(Int32.Parse(Reader.GetValue(2).ToString()));

                if (!String.IsNullOrEmpty(Reader.GetValue(3).ToString()) || !String.IsNullOrEmpty(Reader.GetValue(4).ToString()))
                {
                    curThreshold.lowValue = Convert.ToDouble(Reader.GetValue(3).ToString());
                    curThreshold.highValue = Convert.ToDouble(Reader.GetValue(4).ToString());
                }

                thresholds.Add(curThreshold);
            }//while

            connection.Close();
            this.thresholds = thresholds;
        }//getThreshold

        /// <summary>
        /// Author:Ray Crego
        /// Gets VineyardBlocks from DataBase
        /// </summary>
        /// <returns></returns>
        List<VineyardBlock> getVineyardBlocks()
        {
            List<VineyardBlock> vineyardBlocks = new List<VineyardBlock>();

            string connectionString = "datasource=localhost;username=tara;password=tarapass;database=vino";
            MySqlConnection connection = new MySqlConnection(connectionString);
            MySqlCommand command = connection.CreateCommand();
            MySqlDataReader Reader;
            command.CommandText = "SELECT VineyardBlockID from tblvineyardblock";
            connection.Open();
            Reader = command.ExecuteReader();

            VineyardBlock curVineyardBlock;

            while (Reader.Read())
            {
                curVineyardBlock = new VineyardBlock(Convert.ToInt32(Reader.GetValue(0)));
                vineyardBlocks.Add(curVineyardBlock);
            }//while

            connection.Close();
            return vineyardBlocks;
        }//getVineyardBlock

        /// <summary>
        /// Author:Ray Crego
        /// This function sends an alert out to the user's email
        /// </summary>
        /// <param name="alert"></param>
        public void sendAlert(Alert alert)
        {
            Threshold threshhold = alert.threshold;
            User user = threshhold.user;
            MySqlConnection conn;
            MySqlDataReader Reader;
            Nullable<DateTime> lastAlert = new Nullable<DateTime>();
            conn = new MySqlConnection("datasource=localhost;username=tara;password=tarapass;database=vino");
            bool send = true;

            MySqlCommand sqlcommand = new MySqlCommand("SELECT AlertCreated FROM tblalert WHERE ThresholdID = '" + alert.threshold.thesholdID + "'", conn);
            try
            {
                conn.Open();
                Reader = sqlcommand.ExecuteReader();
                while (Reader.Read())
                {
                    string t = Reader.GetValue(0).ToString();
                    lastAlert = DateTime.Parse(t);
                }
                conn.Close();

            }//try
            catch (MySqlException ex)
            {
                MessageBox.Show(ex.Message);
            }//catch

            List<string> userEmails = new List<string>();
            MySqlCommand users = new MySqlCommand("SELECT Email FROM tbluser", conn);
            try
            {
                conn.Open();
                Reader = users.ExecuteReader();
                while (Reader.Read())
                {
                    userEmails.Add(Reader.GetValue(0).ToString());
                }
                conn.Close();

            }//try
            catch (MySqlException ex)
            {
                MessageBox.Show(ex.Message);
            }//catch


            if (lastAlert.HasValue)
            {
                TimeSpan span = DateTime.Now.Subtract(lastAlert.Value);
                if (span.TotalMinutes < 60)
                    send = false;
            }

            if (send == true)
            {
                try
                {

                    conn.Open();

                    MySqlCommand mycmd = new MySqlCommand("INSERT INTO vino.tblalert (`AlertID` ,  `VineyardBlockID` ,  `ThresholdID` ,  `CurrentValue` ,  `AlertName` ,  `AlertDescription`, `AlertCreated`) VALUES (NULL , '" + threshhold.vineyardBlock.blockID.ToString() + "' ,'" + threshhold.thesholdID.ToString() + "', '" + alert.currentValue.ToString() + "', '" + alert.alertName.ToString() + "',  '" + alert.alertDescription.ToString() + "', '" + DateTime.Now.ToString("s") + "')", conn);
                    mycmd.ExecuteNonQuery();

                    conn.Close();

                }//try
                catch (MySqlException ex)
                {
                    MessageBox.Show(ex.Message);
                }//catch

                mail = new MailMessage();
                mail.From = new MailAddress("vino.tapteil@gmail.com");

                foreach (string curUser in userEmails)
                {
                    if (!String.IsNullOrEmpty(curUser))
                    {
                        mail.To.Add(new MailAddress(curUser));
                    }
                }


                //Creates mail message subject & body
                mail.Subject = "Vino Alert";
                mail.Body = "Threshold exceeded in Vineyard block:" + alert.vineyardBlockID.ToString() + ".";
                mail.Body += "Threshold Description: " + alert.alertDescription;
                mail.Body += threshhold.thresholdType.thresholdName + "exceeded.";
                mail.Body += "Range: " + threshhold.lowValue.ToString() + "-" + threshhold.highValue.ToString();
                mail.Body += "Current Value: " + alert.currentValue.ToString();

                try
                {
                    smtp.Send(mail);
                }//try
                catch (SmtpFailedRecipientsException ex)
                {
                    for (int i = 0; i < ex.InnerExceptions.Length; i++)
                    {
                        SmtpStatusCode status = ex.InnerExceptions[i].StatusCode;
                        if (status == SmtpStatusCode.MailboxBusy ||
                            status == SmtpStatusCode.MailboxUnavailable)
                        {
                            Console.WriteLine("Delivery failed - retrying in 5 seconds.");
                            System.Threading.Thread.Sleep(5000);
                            smtp.Send(mail);
                        }//if
                        else
                        {
                            Console.WriteLine("Failed to deliver message to {0}",
                                ex.InnerExceptions[i].FailedRecipient);
                        }//else
                    }//for
                }//catch
                catch (Exception ex)
                {
                    Console.WriteLine("Exception caught in RetryIfBusy(): {0}",
                            ex.ToString());
                }//catch
            }//if
        }//sendAlert


        /// <summary>
        ///  Name: Compare
        /// Author: Morgan Hansen & Ray Crego
        /// This function calles current data compare to poll 
        /// the blackbox then compares new vineyard values to 
        /// threshold values and sends Alerts to the user if 
        /// the threshold has been breached.  
        /// </summary>
        public void Compare()
        {
            List<Threshold> curThresholds;

            foreach (CurrentData curData in currentDataList)
            {
                if (curData.GetBlackBox().MonitorCommands())
                {
                    curData.SetWaterStatus(!curData.GetWaterStatus());
                }
                
                if (curData.Compare())
                {

                    curThresholds = thresholds.FindAll(delegate(Threshold t) { return t.vineyardBlock.blockID == curData.GetVineyardBlockID(); });
                    foreach (Threshold threshold in curThresholds)
                    {
                        switch (threshold.thresholdType.thresholdName)
                        {
                            case "AirTemperature":
                                if ((curData.GetAirTemp() < threshold.highValue) || (curData.GetAirTemp() > threshold.lowValue))
                                    sendAlert(new Alert(curData.GetVineyardBlockID(), threshold, curData.GetAirTemp(), "Air Temperature Alert", "Air temperature has crossed its set thresholds"));
                                break;

                            case "Moisture":
                                if ((curData.GetSoilMoisture() < threshold.highValue) || (curData.GetSoilMoisture() > threshold.lowValue))
                                    sendAlert(new Alert(curData.GetVineyardBlockID(), threshold, curData.GetSoilMoisture(), "Soil Moisture Alert", "Soil moisture has crossed its set thresholds"));
                                break;
                        }//Switch
                    }//foreach
                }//if
            }//foreach
        }//compare
    }//class
}//nameSpace
